﻿Imports Microsoft.Office.Interop
Imports System.Reflection
Imports System.Drawing

Partial Public Class ExcelExporter(Of T)


    Private Function RecursiveHeaderRender(ws As Excel.Worksheet, lp As LastPosition, node As ExportTreeNode, maxdepth As Integer, baseatt As ExportEntityAttribute) As LastPosition
        Dim retp As LastPosition = lp.Clone

        If node.Nodes.Count = 0 Then
            CellRender(CType(ws.Cells(lp.Row, lp.Column), Excel.Range), node)
            ws.Range(ws.Cells(lp.Row, lp.Column), ws.Cells(maxdepth, retp.Column)).Merge()
            CellVisualRender(ws.Range(ws.Cells(lp.Row, lp.Column), ws.Cells(maxdepth, retp.Column)), node)
            retp.Column += 1
        Else
            Dim tp As LastPosition = lp.Clone

            If Not node.ID = Guid.Empty Then
                CellRender(CType(ws.Cells(lp.Row, lp.Column), Excel.Range), node)
                tp.Row += 1
            End If


            For Each n In node.Nodes
                tp = RecursiveHeaderRender(ws, tp, n, maxdepth, baseatt)
            Next

            retp = tp.Clone
            retp.Row = lp.Row

            If Not node.ID = Guid.Empty Then
                ws.Range(ws.Cells(lp.Row, lp.Column), ws.Cells(lp.Row, tp.Column - 1)).Merge()
                CellVisualRender(ws.Range(ws.Cells(lp.Row, lp.Column), ws.Cells(lp.Row, tp.Column - 1)), node)
            End If

        End If

        Return retp
    End Function

    Private Function RecursiveItemRender(ws As Excel.Worksheet, item As Object, lp As LastPosition, node As ExportTreeNode, baseatt As ExportEntityAttribute) As LastPosition
        Dim retp As LastPosition = lp.Clone

        If node.Nodes.Count = 0 Then
            If item Is Nothing Then item = ""
            CellRender(item, CType(ws.Cells(lp.Row, lp.Column), Excel.Range), node)
            CellVisualRender(CType(ws.Cells(lp.Row, lp.Column), Excel.Range), node)
            retp.Column += 1
        Else
            For Each n In node.Nodes
                retp = RecursiveItemRender(ws, n.PropertyInfo.GetValue(item, Nothing), retp, n, baseatt)
            Next
        End If
        Return retp
    End Function

    Private Sub CellRender(r As Excel.Range, node As ExportTreeNode)
        CellRender(IIf(node.MemberAtt.ColumnName = "", node.PropertyInfo.Name, node.MemberAtt.ColumnName), r, node, True)
    End Sub

    Private Sub CellRender(value As Object, r As Excel.Range, node As ExportTreeNode, Optional header As Boolean = False)
        If Not node.MemberAtt.ValueFormatterFuncName = "" And Not header Then
            Try
                Dim T As Type
                If node.MemberAtt.ValueFormatterType Is Nothing Then
                    T = node.PropertyInfo.ReflectedType
                Else
                    T = node.MemberAtt.ValueFormatterType
                End If

                Dim mi As MethodInfo = T.GetMethod(node.MemberAtt.ValueFormatterFuncName, BindingFlags.Static Or BindingFlags.Public)
                r.Value = mi.Invoke(Nothing, New Object() {value})
            Catch ex As Exception
                r.Value = value.ToString
            End Try
        Else
            r.Value = value.ToString
        End If
    End Sub

    Private Sub CellVisualRender(r As Excel.Range, node As ExportTreeNode)
        If Not node.MemberAtt.BackgroundColor = "" Then r.Interior.Color = ColorTranslator.ToOle(ColorTranslate(node.MemberAtt.BackgroundColor))
        If Not node.MemberAtt.ForegroundColor = "" Then r.Font.Color = ColorTranslator.ToOle(ColorTranslate(node.MemberAtt.ForegroundColor))

        If Not node.MemberAtt.BorderWeight = XlBorderWeight.Undefined Then r.BorderAround2(, node.MemberAtt.BorderWeight)
        If Not node.MemberAtt.BorderLineStyle = XlLineStyle.Undefined Then r.Borders.LineStyle = node.MemberAtt.BorderLineStyle
        If Not node.MemberAtt.BorderColor = "" Then r.Borders.Color = ColorTranslator.ToOle(ColorTranslate(node.MemberAtt.BorderColor))

        If Not node.MemberAtt.VAlign = XlVAlign.Undefined Then r.VerticalAlignment = node.MemberAtt.VAlign
        If Not node.MemberAtt.HAlign = XlHAlign.Undefined Then r.HorizontalAlignment = node.MemberAtt.HAlign
    End Sub

    Private Function ColorTranslate(str As String) As Color
        If str = "" Then Return Color.Empty

        If str.StartsWith("#") Then
            Return System.Drawing.ColorTranslator.FromHtml(str)
        Else
            Return Color.FromName(str)
        End If
    End Function

    Private Class LastPosition
        Implements ICloneable

        Sub New(_row As Integer, _column As Integer)
            MyBase.New()
            Row = _row
            Column = _column
        End Sub

        Sub New()
            MyBase.New()
        End Sub

        Property Row As Integer
        Property Column As Integer

        Public Function Clone() As Object Implements System.ICloneable.Clone
            Dim ret As New LastPosition
            ret.Row = Row
            ret.Column = Column
            Return ret
        End Function
    End Class

End Class